//===------- ExistentialTransform.cpp - Transform Existential Args -------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Transform existential parameters to generic ones.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-existential-transform"
#include "ExistentialTransform.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/SIL/OptimizationRemark.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/TypeSubstCloner.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
#include "swift/SILOptimizer/Utils/Existential.h"
#include "swift/SILOptimizer/Utils/Generics.h"
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
#include "swift/SILOptimizer/Utils/SpecializationMangler.h"
#include "llvm/ADT/SmallVector.h"

using namespace swift;

using llvm::SmallDenseMap;
using llvm::SmallPtrSet;
using llvm::SmallVector;
using llvm::SmallVectorImpl;

/// Create a SILCloner for Existential Specilizer.
namespace {
class ExistentialSpecializerCloner
    : public TypeSubstCloner<ExistentialSpecializerCloner,
                             SILOptFunctionBuilder> {
  using SuperTy =
      TypeSubstCloner<ExistentialSpecializerCloner, SILOptFunctionBuilder>;
  friend class SILInstructionVisitor<ExistentialSpecializerCloner>;
  friend class SILCloner<ExistentialSpecializerCloner>;

  SILFunction *OrigF;
  SmallVector<ArgumentDescriptor, 4> &ArgumentDescList;
  SmallDenseMap<int, GenericTypeParamType *> &ArgToGenericTypeMap;
  SmallDenseMap<int, ExistentialTransformArgumentDescriptor>
      &ExistentialArgDescriptor;

  // Use one OpenedArchetypesTracker while cloning.
  SILOpenedArchetypesTracker OpenedArchetypesTracker;

  // AllocStack instructions introduced in the new prolog that require cleanup.
  SmallVector<AllocStackInst *, 4> AllocStackInsts;
  // Temporary values introduced in the new prolog that require cleanup.
  SmallVector<SILValue, 4> CleanupValues;

protected:
  void postProcess(SILInstruction *Orig, SILInstruction *Cloned) {
    SILClonerWithScopes<ExistentialSpecializerCloner>::postProcess(Orig,
                                                                   Cloned);
  }

  void cloneArguments(SmallVectorImpl<SILValue> &entryArgs);

public:
  ExistentialSpecializerCloner(
      SILFunction *OrigF, SILFunction *NewF, SubstitutionMap Subs,
      SmallVector<ArgumentDescriptor, 4> &ArgumentDescList,
      SmallDenseMap<int, GenericTypeParamType *> &ArgToGenericTypeMap,
      SmallDenseMap<int, ExistentialTransformArgumentDescriptor>
          &ExistentialArgDescriptor)
      : SuperTy(*NewF, *OrigF, Subs), OrigF(OrigF),
        ArgumentDescList(ArgumentDescList),
        ArgToGenericTypeMap(ArgToGenericTypeMap),
        ExistentialArgDescriptor(ExistentialArgDescriptor),
        OpenedArchetypesTracker(NewF) {
    getBuilder().setOpenedArchetypesTracker(&OpenedArchetypesTracker);
  }

  void cloneAndPopulateFunction();
};
} // end anonymous namespace

/// This function will create the generic version.
void ExistentialSpecializerCloner::cloneAndPopulateFunction() {
  SmallVector<SILValue, 4> entryArgs;
  entryArgs.reserve(OrigF->getArguments().size());
  cloneArguments(entryArgs);

  // Visit original BBs in depth-first preorder, starting with the
  // entry block, cloning all instructions and terminators.
  auto *NewEntryBB = getBuilder().getFunction().getEntryBlock();
  cloneFunctionBody(&Original, NewEntryBB, entryArgs);

  // Cleanup allocations created in the new prolog.
  SmallVector<SILBasicBlock *, 4> exitingBlocks;
  getBuilder().getFunction().findExitingBlocks(exitingBlocks);
  for (auto *exitBB : exitingBlocks) {
    SILBuilderWithScope Builder(exitBB->getTerminator());
    // A return location can't be used for a non-return instruction.
    auto loc = RegularLocation::getAutoGeneratedLocation();
    for (SILValue cleanupVal : CleanupValues)
      Builder.createDestroyAddr(loc, cleanupVal);

    for (auto *ASI : llvm::reverse(AllocStackInsts))
      Builder.createDeallocStack(loc, ASI);
  }
}

// Gather the conformances needed for an existential value based on an opened
// archetype. This adds any conformances inherited from superclass constraints.
static ArrayRef<ProtocolConformanceRef>
collectExistentialConformances(ModuleDecl *M, CanType openedType,
                               CanType existentialType) {
  assert(!openedType.isAnyExistentialType());

  auto layout = existentialType.getExistentialLayout();
  auto protocols = layout.getProtocols();

  SmallVector<ProtocolConformanceRef, 4> conformances;
  for (auto proto : protocols) {
    auto conformance = M->lookupConformance(openedType, proto->getDecl());
    assert(conformance);
    conformances.push_back(conformance);
  }
  return M->getASTContext().AllocateCopy(conformances);
}

// Create the entry basic block with the function arguments.
void ExistentialSpecializerCloner::cloneArguments(
    SmallVectorImpl<SILValue> &entryArgs) {
  auto &M = OrigF->getModule();

  // Create the new entry block.
  SILFunction &NewF = getBuilder().getFunction();
  SILBasicBlock *ClonedEntryBB = NewF.createBasicBlock();

  /// Builder will have a ScopeClone with a debugscope that is inherited from
  /// the F.
  ScopeCloner SC(NewF);
  auto DebugScope = SC.getOrCreateClonedScope(OrigF->getDebugScope());

  // Setup a NewFBuilder for the new entry block, reusing the cloner's
  // SILBuilderContext.
  SILBuilder NewFBuilder(ClonedEntryBB, DebugScope,
                         getBuilder().getBuilderContext());
  auto InsertLoc = RegularLocation::getAutoGeneratedLocation();

  auto NewFTy = NewF.getLoweredFunctionType();
  SmallVector<SILParameterInfo, 4> params;
  params.append(NewFTy->getParameters().begin(), NewFTy->getParameters().end());

  for (auto &ArgDesc : ArgumentDescList) {
    auto iter = ArgToGenericTypeMap.find(ArgDesc.Index);
    if (iter == ArgToGenericTypeMap.end()) {
      // Clone arguments that are not rewritten.
      auto Ty = params[ArgDesc.Index].getArgumentType(M, NewFTy);
      auto LoweredTy = NewF.getLoweredType(NewF.mapTypeIntoContext(Ty));
      auto MappedTy =
          LoweredTy.getCategoryType(ArgDesc.Arg->getType().getCategory());
      auto *NewArg =
          ClonedEntryBB->createFunctionArgument(MappedTy, ArgDesc.Decl);
      NewArg->setOwnershipKind(ValueOwnershipKind(
          NewF, MappedTy, ArgDesc.Arg->getArgumentConvention()));
      entryArgs.push_back(NewArg);
      continue;
    }
    // Create the generic argument.
    GenericTypeParamType *GenericParam = iter->second;
    SILType GenericSILType =
        NewF.getLoweredType(NewF.mapTypeIntoContext(GenericParam));
    GenericSILType = GenericSILType.getCategoryType(
                                          ArgDesc.Arg->getType().getCategory());
    auto *NewArg = ClonedEntryBB->createFunctionArgument(GenericSILType);
    NewArg->setOwnershipKind(ValueOwnershipKind(
        NewF, GenericSILType, ArgDesc.Arg->getArgumentConvention()));
    // Determine the Conformances.
    SILType ExistentialType = ArgDesc.Arg->getType().getObjectType();
    CanType OpenedType = NewArg->getType().getASTType();
    auto Conformances = collectExistentialConformances(
        M.getSwiftModule(), OpenedType, ExistentialType.getASTType());
    auto ExistentialRepr =
        ArgDesc.Arg->getType().getPreferredExistentialRepresentation();
    auto &EAD = ExistentialArgDescriptor[ArgDesc.Index];
    switch (ExistentialRepr) {
    case ExistentialRepresentation::Opaque: {
      /// Create this sequence for init_existential_addr.:
      /// bb0(%0 : $*T):
      /// %3 = alloc_stack $P
      /// %4 = init_existential_addr %3 : $*P, $T
      /// copy_addr [take] %0 to [initialization] %4 : $*T
      /// %7 = open_existential_addr immutable_access %3 : $*P to
      /// $*@opened P
      auto *ASI =
          NewFBuilder.createAllocStack(InsertLoc, ArgDesc.Arg->getType());
      AllocStackInsts.push_back(ASI);

      auto *EAI = NewFBuilder.createInitExistentialAddr(
          InsertLoc, ASI, NewArg->getType().getASTType(), NewArg->getType(),
          Conformances);

      bool origConsumed = EAD.isConsumed;
      // If the existential is not consumed in the function body, then the one
      // we introduce here needs cleanup.
      if (!origConsumed)
        CleanupValues.push_back(ASI);

      NewFBuilder.createCopyAddr(InsertLoc, NewArg, EAI,
                                 origConsumed ? IsTake_t::IsTake
                                              : IsTake_t::IsNotTake,
                                 IsInitialization_t::IsInitialization);
      entryArgs.push_back(ASI);
      break;
    }
    case ExistentialRepresentation::Class: {
      SILValue NewArgValue = NewArg;
      if (!NewArg->getType().isObject()) {
        NewArgValue = NewFBuilder.createLoad(InsertLoc, NewArg,
                                         LoadOwnershipQualifier::Unqualified);
      }
      
      // FIXME_ownership: init_existential_ref always takes ownership of the
      // incoming reference. If the argument convention is borrowed
      // (!isConsumed), then we should create a copy_value here and add this new
      // existential to the CleanupValues vector.

      ///  Simple case: Create an init_existential.
      /// %5 = init_existential_ref %0 : $T : $T, $P
      SILValue InitRef = NewFBuilder.createInitExistentialRef(
          InsertLoc, ArgDesc.Arg->getType().getObjectType(),
          NewArg->getType().getASTType(),
          NewArgValue, Conformances);
      
      if (!NewArg->getType().isObject()) {
        auto alloc = NewFBuilder.createAllocStack(InsertLoc,
                                                  InitRef->getType());
        NewFBuilder.createStore(InsertLoc, InitRef, alloc,
                                StoreOwnershipQualifier::Unqualified);
        InitRef = alloc;
        AllocStackInsts.push_back(alloc);
      }

      entryArgs.push_back(InitRef);
      break;
    }
    default: {
      llvm_unreachable("Unhandled existential type in ExistentialTransform!");
      break;
    }
    };
  }
}

/// Create a new function name for the newly generated protocol constrained
/// generic function.
std::string ExistentialTransform::createExistentialSpecializedFunctionName() {
  for (auto const &IdxIt : ExistentialArgDescriptor) {
    int Idx = IdxIt.first;
    Mangler.setArgumentExistentialToGeneric(Idx);
  }
  auto MangledName = Mangler.mangle();
  assert(!F->getModule().hasFunction(MangledName));
  return MangledName;
}

/// Convert all existential argument types to generic argument type.
void ExistentialTransform::convertExistentialArgTypesToGenericArgTypes(
    SmallVectorImpl<GenericTypeParamType *> &genericParams,
    SmallVectorImpl<Requirement> &requirements) {

  SILModule &M = F->getModule();
  auto &Ctx = M.getASTContext();
  auto FTy = F->getLoweredFunctionType();

  /// If the original function is generic, then maintain the same.
  auto OrigGenericSig = FTy->getInvocationGenericSignature();

  /// Original list of parameters
  SmallVector<SILParameterInfo, 4> params;
  params.append(FTy->getParameters().begin(), FTy->getParameters().end());

  /// Determine the existing generic parameter depth.
  int Depth = 0;
  if (OrigGenericSig != nullptr) {
    Depth = OrigGenericSig->getGenericParams().back()->getDepth() + 1;
  }

  /// Index of the Generic Parameter.
  int GPIdx = 0;

  /// Convert the protocol arguments of F to generic ones.
  for (auto const &IdxIt : ExistentialArgDescriptor) {
    int Idx = IdxIt.first;
    auto &param = params[Idx];
    auto PType = param.getArgumentType(M, FTy);
    assert(PType.isExistentialType());
    /// Generate new generic parameter.
    auto *NewGenericParam = GenericTypeParamType::get(Depth, GPIdx++, Ctx);
    genericParams.push_back(NewGenericParam);
    Requirement NewRequirement(RequirementKind::Conformance, NewGenericParam,
                               PType);
    requirements.push_back(NewRequirement);
    ArgToGenericTypeMap.insert(
        std::pair<int, GenericTypeParamType *>(Idx, NewGenericParam));
    assert(ArgToGenericTypeMap.find(Idx) != ArgToGenericTypeMap.end());
  }
}

/// Create the signature for the newly generated protocol constrained generic
/// function.
CanSILFunctionType
ExistentialTransform::createExistentialSpecializedFunctionType() {
  auto FTy = F->getLoweredFunctionType();
  SILModule &M = F->getModule();
  auto &Ctx = M.getASTContext();
  GenericSignature NewGenericSig;

  /// If the original function is generic, then maintain the same.
  auto OrigGenericSig = FTy->getInvocationGenericSignature();

  SmallVector<GenericTypeParamType *, 2> GenericParams;
  SmallVector<Requirement, 2> Requirements;

  /// Convert existential argument types to generic argument types.
  convertExistentialArgTypesToGenericArgTypes(GenericParams, Requirements);

  /// Compute the updated generic signature.
  NewGenericSig = evaluateOrDefault(
      Ctx.evaluator,
      AbstractGenericSignatureRequest{
        OrigGenericSig.getPointer(), std::move(GenericParams),
        std::move(Requirements)},
      GenericSignature());

  /// Create a lambda for GenericParams.
  auto getCanonicalType = [&](Type t) -> CanType {
    return t->getCanonicalType(NewGenericSig);
  };

  /// Original list of parameters
  SmallVector<SILParameterInfo, 4> params;
  params.append(FTy->getParameters().begin(), FTy->getParameters().end());

  /// Create the complete list of parameters.
  int Idx = 0;
  SmallVector<SILParameterInfo, 8> InterfaceParams;
  InterfaceParams.reserve(params.size());
  for (auto &param : params) {
    auto iter = ArgToGenericTypeMap.find(Idx);
    if (iter != ArgToGenericTypeMap.end()) {
      auto GenericParam = iter->second;
      InterfaceParams.push_back(SILParameterInfo(getCanonicalType(GenericParam),
                                                 param.getConvention()));
    } else {
      InterfaceParams.push_back(param);
    }
    Idx++;
  }

  // Add error results.
  Optional<SILResultInfo> InterfaceErrorResult;
  if (FTy->hasErrorResult()) {
    InterfaceErrorResult = FTy->getErrorResult();
  }

  /// Finally the ExtInfo.
  auto ExtInfo = FTy->getExtInfo();
  ExtInfo = ExtInfo.withRepresentation(SILFunctionTypeRepresentation::Thin);
  auto witnessMethodConformance = FTy->getWitnessMethodConformanceOrInvalid();

  /// Return the new signature.
  return SILFunctionType::get(
      NewGenericSig, ExtInfo, FTy->getCoroutineKind(),
      FTy->getCalleeConvention(), InterfaceParams, FTy->getYields(),
      FTy->getResults(), InterfaceErrorResult,
      SubstitutionMap(), false,
      Ctx, witnessMethodConformance);
}

/// Create the Thunk Body with always_inline attribute.
void ExistentialTransform::populateThunkBody() {

  SILModule &M = F->getModule();

  F->setThunk(IsSignatureOptimizedThunk);
  F->setInlineStrategy(AlwaysInline);

  /// Remove original body of F.
  for (auto It = F->begin(), End = F->end(); It != End;) {
    auto *BB = &*It++;
    removeDeadBlock(BB);
  }

  /// Create a basic block and the function arguments.
  auto *ThunkBody = F->createBasicBlock();
  for (auto &ArgDesc : ArgumentDescList) {
    auto argumentType = ArgDesc.Arg->getType();
    ThunkBody->createFunctionArgument(argumentType, ArgDesc.Decl);
  }

  /// Builder to add new instructions in the Thunk.
  SILBuilder Builder(ThunkBody);
  SILOpenedArchetypesTracker OpenedArchetypesTracker(F);
  Builder.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
  Builder.setCurrentDebugScope(ThunkBody->getParent()->getDebugScope());

  /// Location to insert new instructions.
  auto Loc = ThunkBody->getParent()->getLocation();

  /// Create the function_ref instruction to the NewF.
  auto *FRI = Builder.createFunctionRefFor(Loc, NewF);

  auto GenCalleeType = NewF->getLoweredFunctionType();
  auto CalleeGenericSig = GenCalleeType->getInvocationGenericSignature();
  auto OrigGenCalleeType = F->getLoweredFunctionType();
  auto OrigCalleeGenericSig =
    OrigGenCalleeType->getInvocationGenericSignature();

  /// Determine arguments to Apply.
  /// Generate opened existentials for generics.
  SmallVector<SILValue, 8> ApplyArgs;
  // Maintain a list of arg values to be destroyed. These are consumed by the
  // convention and require a copy.
  struct Temp {
    SILValue DeallocStackEntry;
    SILValue DestroyValue;
  };
  SmallVector<Temp, 8> Temps;
  SmallDenseMap<GenericTypeParamType *, Type> GenericToOpenedTypeMap;
  for (auto &ArgDesc : ArgumentDescList) {
    auto iter = ArgToGenericTypeMap.find(ArgDesc.Index);
    auto it = ExistentialArgDescriptor.find(ArgDesc.Index);
    if (iter != ArgToGenericTypeMap.end() &&
        it != ExistentialArgDescriptor.end()) {
      ExistentialTransformArgumentDescriptor &ETAD = it->second;
      OpenedArchetypeType *Opened;
      auto OrigOperand = ThunkBody->getArgument(ArgDesc.Index);
      auto SwiftType = ArgDesc.Arg->getType().getASTType();
      auto OpenedType =
          SwiftType->openAnyExistentialType(Opened)->getCanonicalType();
      auto OpenedSILType = NewF->getLoweredType(OpenedType);
      SILValue archetypeValue;
      auto ExistentialRepr =
          ArgDesc.Arg->getType().getPreferredExistentialRepresentation();
      switch (ExistentialRepr) {
      case ExistentialRepresentation::Opaque: {
        archetypeValue = Builder.createOpenExistentialAddr(
            Loc, OrigOperand, OpenedSILType, it->second.AccessType);
        SILValue calleeArg = archetypeValue;
        if (ETAD.isConsumed) {
          // open_existential_addr projects a borrowed address into the
          // existential box. Since the callee consumes the generic value, we
          // must pass in a copy.
          auto *ASI =
            Builder.createAllocStack(Loc, OpenedSILType);
          Builder.createCopyAddr(Loc, archetypeValue, ASI, IsNotTake,
                                 IsInitialization_t::IsInitialization);
          Temps.push_back({ASI, OrigOperand});
          calleeArg = ASI;
        }
        ApplyArgs.push_back(calleeArg);
        break;
      }
      case ExistentialRepresentation::Class: {
        // If the operand is not object type, we need an explicit load.
        SILValue OrigValue = OrigOperand;
        if (!OrigOperand->getType().isObject()) {
          OrigValue = Builder.createLoad(Loc, OrigValue,
                                         LoadOwnershipQualifier::Unqualified);
        }
        // OpenExistentialRef forwards ownership, so it does the right thing
        // regardless of whether the argument is borrowed or consumed.
        archetypeValue =
            Builder.createOpenExistentialRef(Loc, OrigValue, OpenedSILType);
        if (!OrigOperand->getType().isObject()) {
          SILValue ASI = Builder.createAllocStack(Loc, OpenedSILType);
          Builder.createStore(Loc, archetypeValue, ASI,
                              StoreOwnershipQualifier::Unqualified);
          Temps.push_back({ASI, SILValue()});
          archetypeValue = ASI;
        }
        ApplyArgs.push_back(archetypeValue);
        break;
      }
      default: {
        llvm_unreachable("Unhandled existential type in ExistentialTransform!");
        break;
      }
      };
      GenericToOpenedTypeMap.insert(
          std::pair<GenericTypeParamType *, Type>(iter->second, OpenedType));
      assert(GenericToOpenedTypeMap.find(iter->second) !=
             GenericToOpenedTypeMap.end());
    } else {
      ApplyArgs.push_back(ThunkBody->getArgument(ArgDesc.Index));
    }
  }

  unsigned int OrigDepth = 0;
  if (F->getLoweredFunctionType()->isPolymorphic()) {
    OrigDepth = OrigCalleeGenericSig->getGenericParams().back()->getDepth() + 1;
  }
  SubstitutionMap OrigSubMap = F->getForwardingSubstitutionMap();

  /// Create substitutions for Apply instructions.
  auto SubMap = SubstitutionMap::get(
      CalleeGenericSig,
      [&](SubstitutableType *type) -> Type {
        if (auto *GP = dyn_cast<GenericTypeParamType>(type)) {
          if (GP->getDepth() < OrigDepth) {
            return Type(GP).subst(OrigSubMap);
          } else {
            auto iter = GenericToOpenedTypeMap.find(GP);
            assert(iter != GenericToOpenedTypeMap.end());
            return iter->second;
          }
        } else {
          return type;
        }
      },
      MakeAbstractConformanceForGenericType());

  /// Perform the substitutions.
  auto SubstCalleeType = GenCalleeType->substGenericArgs(
      M, SubMap, Builder.getTypeExpansionContext());

  /// Obtain the Result Type.
  SILValue ReturnValue;
  auto FunctionTy = NewF->getLoweredFunctionType();
  SILFunctionConventions Conv(SubstCalleeType, M);
  SILType ResultType = Conv.getSILResultType();

  /// If the original function has error results,  we need to generate a
  /// try_apply to call a function with an error result.
  if (FunctionTy->hasErrorResult()) {
    SILFunction *Thunk = ThunkBody->getParent();
    SILBasicBlock *NormalBlock = Thunk->createBasicBlock();
    ReturnValue =
        NormalBlock->createPhiArgument(ResultType, ValueOwnershipKind::Owned);
    SILBasicBlock *ErrorBlock = Thunk->createBasicBlock();

    SILType Error = Conv.getSILType(FunctionTy->getErrorResult());
    auto *ErrorArg =
        ErrorBlock->createPhiArgument(Error, ValueOwnershipKind::Owned);
    Builder.createTryApply(Loc, FRI, SubMap, ApplyArgs, NormalBlock,
                           ErrorBlock);

    Builder.setInsertionPoint(ErrorBlock);
    Builder.createThrow(Loc, ErrorArg);
    Builder.setInsertionPoint(NormalBlock);
  } else {
    /// Create the Apply with substitutions
    ReturnValue = Builder.createApply(Loc, FRI, SubMap, ApplyArgs);
  }
  auto cleanupLoc = RegularLocation::getAutoGeneratedLocation();
  for (auto &Temp : llvm::reverse(Temps)) {
    // The original argument was copied into a temporary and consumed by the
    // callee as such:
    //   bb (%consumedExistential : $*Protocol)
    //     %valAdr = open_existential_addr %consumedExistential
    //     %temp = alloc_stack $T
    //     copy_addr %valAdr to %temp // <== Temp CopyAddr
    //     apply(%temp)               // <== Temp is consumed by the apply
    //
    // Destroy the original arument and deallocation the temporary:
    //     destroy_addr %consumedExistential : $*Protocol
    //     dealloc_stack %temp : $*T
    if (Temp.DestroyValue)
      Builder.createDestroyAddr(cleanupLoc, Temp.DestroyValue);
    if (Temp.DeallocStackEntry)
      Builder.createDeallocStack(cleanupLoc, Temp.DeallocStackEntry);
  }
  /// Set up the return results.
  if (NewF->isNoReturnFunction()) {
    Builder.createUnreachable(Loc);
  } else {
    Builder.createReturn(Loc, ReturnValue);
  }
}

/// Strategy to specialize existential arguments:
/// (1) Create a protocol constrained generic function from the old function;
/// (2) Create a thunk for the original function that invokes (1) including
/// setting
///     its inline strategy as always inline.
void ExistentialTransform::createExistentialSpecializedFunction() {
  std::string Name = createExistentialSpecializedFunctionName();
  SILLinkage linkage = getSpecializedLinkage(F, F->getLinkage());

  /// Create devirtualized function type.
  auto NewFTy = createExistentialSpecializedFunctionType();

  auto NewFGenericSig = NewFTy->getInvocationGenericSignature();
  auto NewFGenericEnv = NewFGenericSig->getGenericEnvironment();

  /// Step 1: Create the new protocol constrained generic function.
  NewF = FunctionBuilder.createFunction(
      linkage, Name, NewFTy, NewFGenericEnv, F->getLocation(), F->isBare(),
      F->isTransparent(), F->isSerialized(), IsNotDynamic, F->getEntryCount(),
      F->isThunk(), F->getClassSubclassScope(), F->getInlineStrategy(),
      F->getEffectsKind(), nullptr, F->getDebugScope());
  /// Set the semantics attributes for the new function.
  for (auto &Attr : F->getSemanticsAttrs())
    NewF->addSemanticsAttr(Attr);

  /// Set Unqualified ownership, if any.
  if (!F->hasOwnership()) {
    NewF->setOwnershipEliminated();
  }

  /// Step 1a: Populate the body of NewF.
  SubstitutionMap Subs = SubstitutionMap::get(
      NewFGenericSig,
      [&](SubstitutableType *type) -> Type {
        return NewFGenericEnv->mapTypeIntoContext(type);
      },
      LookUpConformanceInModule(F->getModule().getSwiftModule()));
  ExistentialSpecializerCloner cloner(F, NewF, Subs, ArgumentDescList,
                                      ArgToGenericTypeMap,
                                      ExistentialArgDescriptor);
  cloner.cloneAndPopulateFunction();

  /// Step 2: Create the thunk with always_inline and populate its body.
  populateThunkBody();

  assert(F->getDebugScope()->Parent != NewF->getDebugScope()->Parent);

  LLVM_DEBUG(llvm::dbgs() << "After ExistentialSpecializer Pass\n"; F->dump();
             NewF->dump(););
}
